#include "Avalon/Codecs/ZLibDecoder.hpp"
#include <zlib.h>
#include "Avalon/Codecs/DecoderException.hpp"
#include "Avalon/IO/Buffer.hpp"

using namespace Avalon;
using namespace Avalon::Codecs;
using namespace Avalon::IO;

ZLibDecoder::ZLibDecoder() {}

ZLibDecoder::~ZLibDecoder() {}

bool ZLibDecoder::IsDecodeInPlaceSupported() {
  return false;
}

void ZLibDecoder::Decode(const void* source, int sourceSize, void* destination,
    int destinationSize, int* decodedSize) {
  int uncompressedSize = *reinterpret_cast<const int*>(source);
  assert(destinationSize >= uncompressedSize);
  int result = uncompress(reinterpret_cast<Bytef*>(destination),
    reinterpret_cast<uLongf*>(&destinationSize),
    reinterpret_cast<const Bytef*>(source) + sizeof(int), sourceSize);
  if(result != Z_OK) {
    if(result == Z_BUF_ERROR) {
      BOOST_THROW_EXCEPTION(DecoderException(
        "The buffer was not large enough to hold the uncompressed data."));
    } else if(result == Z_MEM_ERROR) {
      BOOST_THROW_EXCEPTION(DecoderException("Insufficient memory."));
    } else if(result == Z_DATA_ERROR) {
      BOOST_THROW_EXCEPTION(DecoderException(
        "The compressed data was corrupted."));
    } else {
      BOOST_THROW_EXCEPTION(DecoderException("Unknown error."));
    }
  }
  if(decodedSize != NULL) {
    *decodedSize = uncompressedSize;
  }
}

void ZLibDecoder::Decode(const void* source, int sourceSize,
    Buffer* destination, int* decodedSize) {
  int uncompressedSize = *reinterpret_cast<const int*>(source);
  destination->Grow(uncompressedSize);
  int result = uncompress(
    reinterpret_cast<Bytef*>(destination->GetMutableData()),
    reinterpret_cast<uLongf*>(&uncompressedSize),
    reinterpret_cast<const Bytef*>(source) + sizeof(int),
    sourceSize - sizeof(int));
  if(result != Z_OK) {
    if(result == Z_BUF_ERROR) {
      BOOST_THROW_EXCEPTION(DecoderException(
        "The buffer was not large enough to hold the uncompressed data."));
    } else if(result == Z_MEM_ERROR) {
      BOOST_THROW_EXCEPTION(DecoderException("Insufficient memory."));
    } else if(result == Z_DATA_ERROR) {
      BOOST_THROW_EXCEPTION(DecoderException(
        "The compressed data was corrupted."));
    } else {
      BOOST_THROW_EXCEPTION(DecoderException("Unknown error."));
    }
  }
  if(decodedSize != NULL) {
    *decodedSize = uncompressedSize;
  }
}
